package jaspect.model;


import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

/**
 *This is ultimate code generation factory check it out buddy
 * @author Abin Baby
 */
public class CodeGenerator {

    private StringBuilder javaSource;

    /**
     * Method is used to set the source java string
     * @param JavaSource
     */
    public void setJavaSource(StringBuilder JavaSource) {
        this.javaSource = JavaSource;
    }
    private int JavaSourceIndex;

    /**
     * This is exclusively for the MEMBER ASPECT
     * @param aspectCode
     * @param cb
     */
    public void generateJavaForMember(String aspectCode, AspectConfiguration cb) {
        String aspectName = cb.getName();
        //getting the methods in the aspect
        int dot = 0;
        int obrace = 0;
        int cbrace = 0;

        Properties parameters = new Properties();
        while (true) {
            String methodName;
            dot = aspectCode.indexOf(".", obrace);
            if (dot == -1) {
                break;
            }
            obrace = aspectCode.indexOf("(", dot + 1);
            methodName = aspectCode.substring(dot + 1, obrace);
            String argumentList;


            cbrace = aspectCode.indexOf(")", obrace + 1);
            if (cbrace == obrace + 1) {
                argumentList = "";
            } else {
                argumentList = aspectCode.substring(obrace + 1, cbrace);
            }
            parameters.setProperty(methodName, argumentList);
        }







        if (cb.getMode() == AspectConfiguration.InterceptMode.member) {
            //if this is a member aspect, insert just before last "}"
            //This the point where the real trasistion occurs
            //Member methods like'insert' is generated very next line 
            String jc = cb.toJavaString(parameters);
            getJavaSource().insert(getJavaSource().lastIndexOf("}"), jc);
            AJCompiler.getInstance().setOutputJava(getJavaSource());
            
        }




    }

    /**
     *  NOT USED 5-02-2010 ABIN BABY
     * Used in the case of method aspect adn it gets the list of objects of the class aspectcode
     * @param aspectList
     */
    public void injectJavaSource(List<AspectCode> aspectList) {
        for (AspectCode ac : aspectList) {

            for (String method : ac.getMethodList()) {
                appendJavaSource(method, ac.getInjectMethod(), ac.getInjectionMode());
            }
        }
    }

    /**
     * used exclusively for METHOD aspect finds exact position of insertion using findMethodPosition()
     * @param aspectCode
     * @param ac
     */
    public void generateJavaForMethod(String aspectCode, AspectConfiguration ac) {
        String aspectName = ac.getName();
        int start = 0;
        int methodStart = 0;
        int methodEnd = 0;
        int aspectStart = 0;
        int aspectEnd = 0;

        List<AspectCode> aspectList = new ArrayList<AspectCode>();

        while (true) {
            String injectionMode;
            String aspectMethod;
            String methods;
            start = aspectCode.indexOf("-", aspectEnd);
            if (start == -1) {
                break;
            }
            methodStart = aspectCode.indexOf("(", start);
            methodEnd = aspectCode.indexOf(")", methodStart);
            aspectStart = aspectCode.indexOf(".", methodEnd);
            aspectEnd = aspectCode.indexOf("}", aspectStart);
            injectionMode = aspectCode.substring(start + 1, methodStart);
            methods = aspectCode.substring(methodStart + 1, methodEnd);
            aspectMethod = aspectCode.substring(aspectStart + 1, aspectEnd);
            AspectCode ap = new AspectCode();
            ap.parser(injectionMode, methods, aspectMethod);

            aspectList.add(ap);
            start = aspectEnd + 1;

        }

        for (AspectCode code : aspectList) {

            int j = 0;
            Iterator i = code.getMethodList().iterator();
            int injectionPoint;
            String injectionCode;
            while (i.hasNext()) {
                String methodName = code.getMethodList().get(j);
                j++;
                i.next();
                injectionPoint = findMethodPosition(methodName, code.getInjectionMode());
                if (injectionPoint == -1) {
                    throw new RuntimeException("Method not found");

                }
                injectionCode = code.toJavaString(methodName, code.getInjectMethod(), ac.getServiceClassName());

                this.getJavaSource().insert(injectionPoint, injectionCode);
                //last line added in the morning not tested
                //this.getJavaSource().insert(0,"try \n{\n");
                
                

            }

        }
        AJCompiler.getInstance().setOutputJava(getJavaSource());
        System.out.println(getJavaSource());
    }

    /**
     * NOT USED
     * Still to be deployed not used till now
     * @param aCodeLine
     * @return
     */
    public Properties generateParameters(String aCodeLine) {

        return null;

    }

    /**
     * NOT USED 5-02-2010 ABIN BABY
     * This is deployed in the case of METHOD ASPECT and is not completely working
     * @param method
     * @param injectMethod
     * @param injectMode
     */
    public void appendJavaSource(String method, String injectMethod, String injectMode) {
        int m = getJavaSource().indexOf(method);
        int point = getJavaSource().indexOf("{", m) + 1;
        if (injectMethod.equals("before")) {
            getJavaSource().insert(point, injectMethod);
        } else if (injectMethod.equals("after")) {


            char ch;
            int flag = 0;
            for (int i = point; i < getJavaSource().length(); i++) {
                ch = getJavaSource().charAt(i);
                if (ch == '{') {
                    flag++;
                } else if (ch == '}') {
                    flag--;
                }


                if (flag == 0) {
                    getJavaSource().insert(i, injectMethod);
                    break;
                }
            }


        } else if (injectMethod.equals("onException")) {
        }
    }

    /**
     * used to find the position before and after the method.
     * @param methodName
     * @param injectionMode
     * @return
     */
    private int findMethodPosition(String methodName, String injectionMode) {
        int position = 0;
        int leftBrace = 0;
        int semi = 0;
        int beforePosition = 0;
        int afterPosition = 0;

        do {


            position = getJavaSource().indexOf(methodName, leftBrace);
            leftBrace = getJavaSource().indexOf("{", position);
            semi = getJavaSource().indexOf(";", position);
        } while (semi < leftBrace);
        position = leftBrace + 1;
        beforePosition = leftBrace + 1;


        //finding the after position
        int flag = 1;

        for (int i = position; i < getJavaSource().length(); i++) {
            if (getJavaSource().charAt(i) == '{') {
                flag++;
            } else if (getJavaSource().charAt(i) == '}') {
                flag--;
            }
            if (flag == 0) {
                afterPosition = i;
                break;
            }
        }


        if (injectionMode.equals("before")) {

            return beforePosition;
        } else if (injectionMode.equals("after")) {
            //   position = javaSource.indexOf("}",leftBrace);


            return afterPosition;


        } else if (injectionMode.equals("onException")) {
            this.getJavaSource().insert(beforePosition, "try{");
            afterPosition = afterPosition + "try{".length();
            this.getJavaSource().insert(afterPosition, " }catch (IOException e) {\n }");
            afterPosition = afterPosition + " }catch (IOException e) {\n ".length();
            return afterPosition;

        }
        position = -1;
        return position;
    }

    /**
     * @return the javaSource
     */
    public StringBuilder getJavaSource() {
        return javaSource;
    }
}

